package com.enlorenz.common.util;

import java.io.UnsupportedEncodingException;
import java.security.InvalidKeyException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.Properties;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;

import com.enlorenz.common.util.FileUtils.IPropIterator;


/**
 * <p>Title: 系统框架</p>
 * <p>Description:加密解密工具 封装了一些加密工具方法, 
 * 包括 3DES, MD5 等 DES加解密程序，desDecrypt解密；
 * desEncrypt加密 MD5加密程序, MD5加密-不可逆的字符串变换算法</p>
 */
public class CryptoUtils {
    private final static String secretKey = "i4j2kj34sdeljksd993jjf22";

    public CryptoUtils() {
    }
    
    
    //加密模式 MD5
    public static final int SIGN_TYPE_MD5=0;
    
    //排序类型 升序
    public static final int SIGN_SORT_ASC=0;
    //降序
    public static final int SIGN_SORT_DESC=1;
    
    /**
	 * 获取签名数据
	 * @param map 数据
	 * @param encoding 编码
	 * @return 获取签名数据
	 */
	public static String getSign(Map<String, String> map,String encoding) {
		return getSign(map, encoding, SIGN_TYPE_MD5, SIGN_SORT_ASC);
	}
    
	/**
	 * 获取签名数据
	 * @param map 数据
	 * @param encoding 编码
	 * @param type 加密模式
	 * @param sort 排序方式
	 * @return 获取签名数据
	 */
	public static String getSign(Map<String, String> map,String encoding,int type,final int sort) {
		if(DataDetUtils.isMapEmpty(map)){
			return "";
		}
		
		if(sort !=SIGN_SORT_ASC && sort != SIGN_SORT_DESC){
			ExceptionUtils.throwRuntimeException("不支持的排序类型:%s", sort);
		}
		
		if(type != SIGN_TYPE_MD5){
			ExceptionUtils.throwRuntimeException("不支持的签名类型:%s", type);
		}
		
		List<Map.Entry<String, String>> entryList= 
				new ArrayList<Map.Entry<String, String>>(map.entrySet());
		
		Collections.sort(entryList,new Comparator<Map.Entry<String, String>>() {
			public int compare(Map.Entry<String, String> mapping1,
					Map.Entry<String, String> mapping2) {
				if(sort == SIGN_SORT_ASC){
					return mapping1.getKey().compareTo(mapping2.getKey());
				}else{
					return mapping2.getKey().compareTo(mapping1.getKey());
				}
			}
		});
		
		StringBuilder buff=new StringBuilder(entryList.size()*20);
		for(Map.Entry<String, String> entry:entryList){
			buff.append(entry.getKey());
			buff.append(StringUtils.trimToEmpty(entry.getValue()));
		}
		
		if(type == SIGN_TYPE_MD5){
			return md5(buff.toString(),encoding);
		}
		return null;
	}
	
    
	
    
    /**
     * 解密Properties
     * @param deskey 关键字
     * @param properties 属性
     * @param allowKeyList 允许的关键字列表
     */
    public static void desDecryptProperties(final String deskey,Properties properties,final String [] allowKeyList){
    	FileUtils.iteratorProperties(properties, new IPropIterator() {
			@Override
			public void iterate(Properties properties, String key, String value) {
				if(null!=allowKeyList
						&&!CommonUtils.compareKeyArr(key, allowKeyList)){
					return;
				}
				value=value.trim();

    			String val=DesUtils.decrypt(deskey, value);
    			properties.setProperty(key, val);  
			}
		});
    }
    
    /**
     * 解密Properties
     * @param deskey 关键字
     * @param properties 属性
     */
    public static void desDecryptProperties(final String deskey,Properties properties){
    	desDecryptProperties(deskey, properties, null);
    }
    
    /**
     * 创建加密字符串
     * @param deskey 关键字
     * @param properties 属性
     * @param allowKeyList 允许的关键字列表
     */
    public static void encryptProperties(final String deskey,Properties properties,final String [] allowKeyList){
    	FileUtils.iteratorProperties(properties, new IPropIterator() {
			@Override
			public void iterate(Properties properties, String key, String value) {
				if(null!=allowKeyList
						&&!CommonUtils.compareKeyArr(key, allowKeyList)){
					return;
				}
				value=value.trim();

    			String val=DesUtils.encrypt(deskey, value);
    			properties.setProperty(key, val);  
			}
		});
    }
    
    /**
     * 创建加密字符串
     * @param deskey 关键字
     * @param properties 属性
     */
    public static void encryptProperties(final String deskey,Properties properties){
    	encryptProperties(deskey, properties, null);
    }
    
    
    /**
     * 加密
     * @param data 数据
     * @param key 关键字
     * @return 加密后的数据
     */
    public static String tDesEncrypt(String data,String key){
    	byte [] result=tDesEncrypt(CodeUtils.hexString2Bytes(data),
    			CodeUtils.hexString2Bytes(key));
    	return CodeUtils.bytes2HexString(result);
    }
    
    /**
     * 加密
     * @param data 数据
     * @param key 关键字
     * @return 加密后的数据
     */
    public static byte[] tDesEncrypt(byte[] data,byte[] key){
    	try{
	    	SecretKey deskey = new SecretKeySpec(key, "DESede");
	    	Cipher cipher = Cipher.getInstance("DESede/ECB/NoPadding");
	    	cipher.init(Cipher.ENCRYPT_MODE, deskey);
	    	return cipher.doFinal(data);
    	}catch (Exception e) {
			throw new RuntimeException(e);
		}
    }
    
    /**
     * 解密
     * @param data 数据
     * @param key 关键字
     * @return 解密后的数据
     */
    public static String tDesDecrypt(String data,String key){
    	byte [] result= tDesDecrypt(CodeUtils.hexString2Bytes(data),
    			CodeUtils.hexString2Bytes(key));
    	return CodeUtils.bytes2HexString(result);
    }
    
    /**
     * 解密
     * @param data 数据
     * @param key 关键字
     * @return 解密后的数据
     */
    public static byte[] tDesDecrypt(byte[] data,byte[] key){
    	try{
	    	SecretKey deskey = new SecretKeySpec(key, "DESede");
	    	Cipher cipher = Cipher.getInstance("DESede/ECB/NoPadding");
	    	cipher.init(Cipher.DECRYPT_MODE, deskey);
	    	return cipher.doFinal(data);
    	}catch (Exception e) {
			throw new RuntimeException(e);
		}
    }
    
    private static final char[] hexDigits = {'0', '1', '2', '3', '4', '5', '6',
    	'7', '8', '9', 'A', 'B', 'C','D', 'E', 'F'};
    
   /**
    *  MD5加密 - 不可逆的字符串变换算法
    * 将“字节串”变换成一个128bit的大整数,表示成16进制的ASCII需要32个字符
    * @param text 字符串
    * @param encoding 字符编码
    * @return 加密后的字符串
    */
    public final static String md5(String text,String encoding) {
    	try {
            byte[] strTemp = text.getBytes(encoding);
            MessageDigest mdTemp = MessageDigest.getInstance("MD5");
            mdTemp.update(strTemp);
            byte[] md = mdTemp.digest();
            int j = md.length;
            char[] str = new char[j * 2];
            int k = 0;
            for (int i = 0; i < j; i++) {
                byte byte0 = md[i];
                str[k++] = hexDigits[byte0 >>> 4 & 0xf];
                str[k++] = hexDigits[byte0 & 0xf];
            }
            return new String(str);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
    
    /**
     *  MD5加密 - 不可逆的字符串变换算法
     * 将“字节串”变换成一个128bit的大整数,表示成16进制的ASCII需要32个字符
     * 默认UTF-8字符编码
     * @param text 字符串
     * @return 加密后的字符串
     */
    public final static String md5(String text) {
    	return md5(text, CommonContants.UTF_8_CHARSET);
    }

    /**
     * 3DES 加密（密钥固定）
     * @param String 明文串
     * @return String 密文串
     * @throws Exception
     */
    public static String desEncrypt(String src) throws Exception {
        javax.crypto.Cipher cipher = javax.crypto.Cipher.getInstance("DESede");
        javax.crypto.SecretKey key;
        byte[] bytReturn;
        key = genDESKey(secretKey.getBytes());
        cipher.init(javax.crypto.Cipher.ENCRYPT_MODE, key);
        bytReturn = cipher.doFinal(src.getBytes());

        return base64Encode(bytReturn);
    }

    /**
     * 3DES 加密（密钥固定）
     * @param String 明文串
     * @param StringBuffer 异常消息
     *
     * @return String 密文串
     */
    public static String desEncrypt(String src, StringBuffer sbfMessage) {
        byte[] bytReturn = null;

        try {
            javax.crypto.Cipher cipher = javax.crypto.Cipher.getInstance(
                    "DESede");
            javax.crypto.SecretKey key;
            key = genDESKey(secretKey.getBytes());
            cipher.init(javax.crypto.Cipher.ENCRYPT_MODE, key);
            bytReturn = cipher.doFinal(src.getBytes());
        } catch (NoSuchAlgorithmException e1) {
            sbfMessage.append(e1.getMessage());
        } catch (NoSuchPaddingException e2) {
            sbfMessage.append(e2.getMessage());
        } catch (InvalidKeyException e3) {
            sbfMessage.append(e3.getMessage());
        } catch (IllegalBlockSizeException e4) {
            sbfMessage.append(e4.getMessage());
        } catch (BadPaddingException e5) {
            sbfMessage.append(e5.getMessage());
        } catch (Exception e6) {
            sbfMessage.append(e6.getMessage());
        }

        return base64Encode(bytReturn);
    }

    /**
     * 3DES加密（密钥动态产生）
     * @param String 明文串
     * @param String 密钥源串
     * @return String 密文串
     * @throws Exception
     */
    public static String desEncrypt(String src, String keysrc)
        throws Exception {
        byte[] bytReturn = null;
        javax.crypto.Cipher cipher = javax.crypto.Cipher.getInstance("DESede");

        javax.crypto.SecretKey key;
        int keysrcLength = keysrc.length();

        if (keysrcLength < 24) {
            for (int i = keysrcLength; i < 24; i++) {
                keysrc = keysrc + keysrc;
            }

            keysrc = keysrc.substring(0, 24);
        }

        key = genDESKey(keysrc.getBytes());

        cipher.init(javax.crypto.Cipher.ENCRYPT_MODE, key);
        bytReturn = cipher.doFinal(src.getBytes());

        return base64Encode(bytReturn);
    }

    /**
     * 3DES加密（密钥动态产生）
     * @param String 明文串
     * @param String 密钥源串
     * @param StringBuffer 异常消息
     * @return String 密文串
     */
    public static String desEncrypt(String src, String keysrc,
        StringBuffer sbfMessage) {
        byte[] bytReturn = null;

        try {
            javax.crypto.Cipher cipher = javax.crypto.Cipher.getInstance(
                    "DESede");

            javax.crypto.SecretKey key;
            int keysrcLength = keysrc.length();

            if (keysrcLength < 24) {
                for (int i = keysrcLength; i < 24; i++) {
                    keysrc = keysrc + keysrc;
                }

                keysrc = keysrc.substring(0, 24);
            }

            key = genDESKey(keysrc.getBytes());

            cipher.init(javax.crypto.Cipher.ENCRYPT_MODE, key);
            bytReturn = cipher.doFinal(src.getBytes());
        } catch (NoSuchAlgorithmException e1) {
            sbfMessage.append(e1.getMessage());
        } catch (NoSuchPaddingException e2) {
            sbfMessage.append(e2.getMessage());
        } catch (InvalidKeyException e3) {
            sbfMessage.append(e3.getMessage());
        } catch (IllegalBlockSizeException e4) {
            sbfMessage.append(e4.getMessage());
        } catch (BadPaddingException e5) {
            sbfMessage.append(e5.getMessage());
        } catch (Exception e6) {
            sbfMessage.append(e6.getMessage());
        }

        return base64Encode(bytReturn);
    }

    /**
     * 3DES 解密（密钥固定）
     * @param String 密文串
     * @return String 明文串
     * @throws Exception
     */
    public static String desDecrypt(String crypt) throws Exception {
        javax.crypto.Cipher cipher = javax.crypto.Cipher.getInstance("DESede");
        javax.crypto.SecretKey key;
        key = genDESKey(secretKey.getBytes());

        byte[] bytReturn;
        cipher.init(javax.crypto.Cipher.DECRYPT_MODE, key);
        bytReturn = cipher.doFinal(base64DecodeToBytes(crypt));

        return base64Decode(base64Encode(bytReturn));
    }

    /**
     * 3DES 解密（密钥固定）
     * @param String 密文串
     * @param StringBuffer 异常消息
     *
     * @return String 明文串
     */
    public static String desDecrypt(String crypt, StringBuffer sbfMessage) {
        byte[] bytReturn = null;

        try {
            javax.crypto.Cipher cipher = javax.crypto.Cipher.getInstance(
                    "DESede");
            javax.crypto.SecretKey key;
            key = genDESKey(secretKey.getBytes());
            cipher.init(javax.crypto.Cipher.DECRYPT_MODE, key);
            bytReturn = cipher.doFinal(base64DecodeToBytes(crypt));
        } catch (NoSuchAlgorithmException e1) {
            sbfMessage.append(e1.getMessage());

            return "";
        } catch (NoSuchPaddingException e2) {
            sbfMessage.append(e2.getMessage());

            return "";
        } catch (InvalidKeyException e3) {
            sbfMessage.append(e3.getMessage());

            return "";
        } catch (IllegalBlockSizeException e4) {
            sbfMessage.append(e4.getMessage());

            return "";
        } catch (BadPaddingException e5) {
            sbfMessage.append(e5.getMessage());

            return "";
        } catch (Exception e6) {
            sbfMessage.append(e6.getMessage());

            return "";
        }

        return base64Decode(base64Encode(bytReturn));
    }

    /**
     * 3DES 解密（密钥动态产生）
     * @param String 密文串
     * @param String 密钥源串
     * @return String 明文串
     * @throws Exception
     */
    public static String desDecrypt(String crypt, String keysrc)
        throws Exception {
        byte[] bytReturn = null;
        javax.crypto.Cipher cipher = javax.crypto.Cipher.getInstance("DESede");
        javax.crypto.SecretKey key;
        int keysrcLength = keysrc.length();

        if (keysrcLength < 24) {
            for (int i = keysrcLength; i < 24; i++) {
                keysrc = keysrc + keysrc;
            }

            keysrc = keysrc.substring(0, 24);
        }

        key = genDESKey(keysrc.getBytes());
        cipher.init(javax.crypto.Cipher.DECRYPT_MODE, key);
        bytReturn = cipher.doFinal(base64DecodeToBytes(crypt));

        return base64Decode(base64Encode(bytReturn));
    }

    /**
     * 3DES 解密（密钥动态产生）
     * @param String 密文串
     * @param String 密钥源串
     * @param StringBuffer 异常消息
     *
     * @return String 明文串
     */
    public static String desDecrypt(String crypt, String keysrc,
        StringBuffer sbfMessage) {
        byte[] bytReturn = null;
        String strReturn = "";

        try {
            javax.crypto.Cipher cipher = javax.crypto.Cipher.getInstance(
                    "DESede");
            javax.crypto.SecretKey key;
            int keysrcLength = keysrc.length();

            if (keysrcLength < 24) {
                for (int i = keysrcLength; i < 24; i++) {
                    keysrc = keysrc + keysrc;
                }

                keysrc = keysrc.substring(0, 24);
            }

            key = genDESKey(keysrc.getBytes());
            cipher.init(javax.crypto.Cipher.DECRYPT_MODE, key);
            bytReturn = cipher.doFinal(base64DecodeToBytes(crypt));
            strReturn = base64Decode(base64Encode(bytReturn));
        } catch (NoSuchAlgorithmException e1) {
            sbfMessage.append(e1.getMessage());
        } catch (NoSuchPaddingException e2) {
            sbfMessage.append(e2.getMessage());
        } catch (InvalidKeyException e3) {
            sbfMessage.append(e3.getMessage());
        } catch (IllegalBlockSizeException e4) {
            sbfMessage.append(e4.getMessage());
        } catch (BadPaddingException e5) {
            sbfMessage.append(e5.getMessage());
        } catch (Exception e6) {
            sbfMessage.append(e6.getMessage());
        }

        return strReturn;
    }

    /**
     * 生成3DES密钥.
     * @param byte[]
     * @return SecretKey
     */
    public static SecretKey genDESKey(byte[] byteKey) {
        SecretKey k = null;
        k = new SecretKeySpec(byteKey, "DESede");

        return k;
    }

    /**
     * BASE64 编码.
     * @param src 数据
     * @return 编码后的字符串
     */
    public static String base64Encode(String src,String encoding) {
        try {
			return base64Encode(src.getBytes(encoding));
		} catch (UnsupportedEncodingException e) {
			throw new RuntimeException(e);
		}
    }
    
    /**
     * BASE64 编码.
     * @param src 数据
     * @return 编码后的字符串
     */
    public static String base64Encode(String src) {
		return base64Encode(src.getBytes());
    }

    /**
     * BASE64 编码(byte[]).
     * @param src 数据
     * @return 编码后的字符串
     */
    @SuppressWarnings("restriction")
    public static String base64Encode(byte[] src) {
		sun.misc.BASE64Encoder encoder = new sun.misc.BASE64Encoder();
        return encoder.encode(src);
    }

    /**
     * BASE64 解码.
     * @param src 密文
     * @param encoding 字符编码
     * @return 解码后的字符串
     */
    public static String base64Decode(String src,String encoding) {
        try {
            return new String(base64DecodeToBytes(src),encoding);
        } catch (Exception ex) {
        	throw new RuntimeException(ex);
        }
    }
    
    /**
     * BASE64 解码.
     * @param src 密文
     * @return 解码后的字符串
     */
    public static String base64Decode(String src) {
        try {
            return new String(base64DecodeToBytes(src));
        } catch (Exception ex) {
        	throw new RuntimeException(ex);
        }
    }

    /**
     * BASE64 解码(to byte[]).
     * @param src
     * @return 解码后的byte数组
     */
    @SuppressWarnings("restriction")
    public static byte[] base64DecodeToBytes(String src) {
		sun.misc.BASE64Decoder decoder = new sun.misc.BASE64Decoder();
        try {
            return decoder.decodeBuffer(src);
        } catch (Exception ex) {
        	throw new RuntimeException(ex);
        }
    }

    /**
     * 对给定字符进行 URL 编码.
     * @param String
     * @return String
     */
    public static String urlEncode(String src) {
        try {
            src = java.net.URLEncoder.encode(src, "GBK");

            return src;
        } catch (Exception ex) {
            ex.printStackTrace();
        }

        return src;
    }

    /**
     * 对给定字符进行 URL 解码
     * @param String 解码前的字符串
     * @return String 解码后的字符串
     */
    public String urlDecode(String value) {
        try {
            return java.net.URLDecoder.decode(value, "GBK");
        } catch (Exception ex) {
            ex.printStackTrace();
        }

        return value;
    }

}
